package polish;

public class Postfix {

	int result, operand;
	
	public Postfix()
	{
		
	}
	
	/**
	 * pre: legal operators are * - + / ^
	 * no whitespace between arguments allowed. 
	 */
	
	public String inToPost(String s)
	{
		String pfx = "";
		Stack<Character> stk = new Stack<Character>();
		
		for (int i = 0; i<s.length(); i++)
		{
			char c = s.charAt(i);
			if(Character.isDigit(c))
			{
				pfx += c;
			}
			else
			{
				if( c == '(')
				{
						// new Substring for recursion starts after open parenthesis stops before last parenthesis
						String newS;
						if (s.lastIndexOf(")") == -1)
							{
							newS = s.substring(i+1);
							}
						else	{
							newS = s.substring(i+1, s.lastIndexOf(")"));
						}
						// I know this it is bad to manipulate a value that is used in the for construct
						// but I couldn't think of another way
						// if there is string left after the closed parenthesis add it to postfix omitting the )
						// the open parenthesis is kept otherwise the index i would be at the false position for 
						// the further evaluation
						if (s.lastIndexOf(")") == -1 || s.lastIndexOf(")"+1) < s.length()-1)
						{	
						s = s.substring(0, s.indexOf("(")+1);
						}
						// if the closed parenthesis is a the end of the string discard the )
						else
						{
						   s = s.substring(0, s.indexOf("(")+1) + s.substring(s.lastIndexOf(")")+1);

						}
						pfx += inToPost(newS);
				 // if a closed parenthesis occurs within a substring never at the end of it 
				// since at the end it is omitted - see above - the stack is emptied and added to the pfx
				}else if(c == ')')
				{
					while (!stk.isEmpty())
					{
						try {
							pfx += stk.pop();
						} catch (StackEmptyException e) {
							// should never happen
							e.printStackTrace();
						}
					}
					
				}else
				{	
					// since the character is neither a digit nor a parenthesis it must be an operator
					// assuming some kind of input validation is performed
					// if the operator has precedence it is pushed onto the stack
					if(stk.isEmpty())
					{
						try {
							stk.push(c);
						} catch (StackOverflowException e) {
							// should never happen
							e.printStackTrace();
						}
					} else
						try {
							if (hasPre(c, stk.peek()))
							{
								stk.push(c);
							}
							// if not the operator 
							else
							{
								while ( !stk.isEmpty() && !hasPre(c, stk.peek()))
									{
									pfx += stk.pop();
									}
									stk.push(c);
							}
						} catch (StackEmptyException e) {
							// in case of invalid input
							e.printStackTrace();
						} catch (StackOverflowException e) {
							// in case of invalid input
							e.printStackTrace();
						}	
				}	
			}
		}
		while(!stk.isEmpty())
		{
			try {
				pfx += stk.pop();
			} catch (StackEmptyException e) {
				// should never happen
				e.printStackTrace();
			}
		}
		
		
		return pfx;
	}
	
	private boolean hasPre(char c, char d)
	{
		switch (c)
		{	
					// has precedence over +, -
		case ('*'): 	
		case ('/'): return d != '^' && d !='*' && d != '/';
					
					// has precedence 
		case ('+'):
		case ('-'):	return false;
			
		case ('^'): return true;
					
		default: return false;
			
		}
	
	}
	
	
	
	
	public int evaluate(String postfix)
	{
		
		Stack<Integer> stack = new Stack<Integer>();
		// if the parameter string contains whitespace it is removed
		//String toEval = StringTrans.removeWhitespace(postfix);
		String toEval = postfix;
		
		for(int i = 0; i< postfix.length(); i++)
		{
			if (Character.isDigit(toEval.charAt(i)))
			{
				
				try {
					stack.push(Integer.parseInt(String.valueOf(toEval.charAt(i))));
				} catch (NumberFormatException e) {
					// in case of illegal user input
					e.printStackTrace();
				} catch (StackOverflowException e) {
					// in case of illegal user input
					e.printStackTrace();
				}	
			}
			else 
			{
				// try catch for popping 2 elements of the stack 
				
				try {
					operand = stack.pop();
				} catch (StackEmptyException e) {
					// in case of illegal user input
					e.printStackTrace();
				}
				
				
				
				{
				try {
					result = stack.pop();
				} catch (StackEmptyException e) {
					// should never happen
					e.printStackTrace();
				}
				}
				switch (toEval.charAt(i))
				{
					case '+':	 result+=operand;
								break;
					case '-': 	result-=operand;
								break;
					case '*':	result*=operand;
								break;
					case '/':	result/=operand;
								break;
					case '^':	result= (int) Math.pow((double) result, (double) operand);
								break;
				}
				
				try {
					stack.push(result);
				} catch (StackOverflowException e) {
					// won't happen with the way the Stack is implemented
					e.printStackTrace();
				}
				
			}
		}
		
		int res = -1;
		try {
			res = stack.pop();
		} catch (StackEmptyException e) {
			// in case of illegal user input
			
			e.printStackTrace();
		}	
		return res;
	}
	
	
	
}
